2

NPM的作用: Node.js程序依赖包的发布、管理和安装。

CommonJS规范

require

require是一个函数,参数是模块标识符,返回值是所引用模块暴露给外部使用者的内容。

直白一点讲:

一个模块定义文件module1.js

module.exports={
    a:1,
    f: function(){
        console.log('f');
    }
}

一个引用模块module1.js的文件

let m1 = require('module1.js');
console.log(m1);
// {a: 1, f: ƒ}

模块的上下文

在每个模块中,都有如下一些事物:

  1. require函数
  2. exports对象,这个对象上挂载的东西会被暴露给外部。
  3. module对象,里面包含模块自身的一些信息,例如ID

![module对象实例]()

Note: exports 对象实际上指向 module.exports的引用,如果随意替换module.exports的引用,则会导致二者的实际引用不相同。不了解这个问题的话,在世纪城使用中,可能会遇到一些难以预料的问题。

模块标识

模块标识是一个字符串,理论上应该是小驼峰式的命名方式,或者以“.” , “..”及“/”开头的路径。
如果模块标识是文件路径,理论上不需要在文件名后面加后缀“.js”。

未指定的约定

在符合CommonJS规范的情况下,有一些机制可以随意实现。之所以这么说,是因为实际上这些东西也是构建完整模块所必须的,但CommonJS并未严格定义这些东西,理论上只要能够完成既定目标就可以。

  1. 模块的存储方案,模块但内容可以存储在数据库、文件系统、工厂函数甚至一个链接库中;
  2. 模块加载器可以支持PATH环境变量用于加载时但寻址,也可以不支持,不作强制限制

Node.js的模块

Node程序通常就是由一个个的模块拼装起来的。这些模块可能的形式: js文件、json文件、C++模块的二进制文件.node

这些模块都是通过 require函数 引入。

Node中的模块查找

Node中的模块分为两类: 核心模块 和 文件模块。

核心模块存在于Node源码,属于Node自带模块。主要是一些基础性的模块,例如:fs, http等。它们查找非常简单,如果require中的模块标志符与这些核心模块的名字吻合,则直接返回这些核心模块export出的内容。

文件模块是需要通过查找文件来获得的模块。这些模块都是第三方开发者编写的程序,并不属于Node本身的一部分。它们是你在编写Node程序(基于Node.js运行的程序, Not Node.js self)时,引入的文件。

文件模块又可以分成 第三方模块项目模块

如果传入require函数是一个文件路径,一般可以看作是项目模块。对于项目模块,如果路径中指定了具体文件,则直接引入。如果路径是一个目录,则首先查找是否存在package.json文件,如果存在且在package.json中定义了main属性,则根据main属性的值查找模块的入口文件,如果没有,则依次查找目录下的index.js, index.json以及index.node

如果传入require函数的是一个非路径的值,则可以看作 第三方模块。Node查找第三方模块也有一套规则存在。

  1. 当前文件(require函数所在的文件)的 node_modules目录;
  2. 当前文件父级目录下的 node_modules下,如果没有,则继续向上级目录,直至根目录下的node_modules;

找到对应的模块后,实际上也就等于获取到了模块的文件PATH,然后就会查找模块的入口文件(查找规则跟项目模块一样),获取模块export的内容。

模块缓存

Node会缓存require的模块,也就是说如果某个模块被加载过,当在有其他模块引用该模块的时候,Node会直接读取缓存中的内容返回。

Node的包机制

包描述文件package.json

主要内容之前有写过。

包目录结构

  • package.json在根目录下
  • 二进制文件放在bin目录下
  • JavaScript源码放在礼拜目录下
  • 文档放在doc目录下
  • 单元测试文件放在test目录下

Node.js/ NPM下的包

NPM(Node.js Package Manager)。

在NPM中存在一个包信息描述文件————package.json。这个文件会记录这个包的其它依赖。而且它的第三方模块会被放到node_modules目录中,这就导致有些开发者会把这个目录也作为项目的一部分提交到git仓库,但我们实际并不推崇这么做。因为node_modules提供的文件是项目运行时所需,且通常情况下,它的内容会比较多。我们可以根据pacakge.json文件的描述,在运行项目之前安装这些依赖(一般通过npm命令npm install)。

如果你对第三方模块的源码有所改动,推荐的做法是在项目初始化时或者其他比较合适的时机进行hack。

NPM下的package.json文件是基于CommonJS定义的包描述文件来实现的。但它有一些地方是与其不同的。这一点需要我们明确。

NPM

在NPM中,NPM2与NPM3是有区别的,NPM2的依赖安装路径是嵌套式的,适合Node.js进行工具、后端的开发。在NPM2中,不同的的包如果使用名称相同但版本不同的的第三方模块,那么这个模块的两个版本分别被放在各自引用者的node_modules目录中,互不干扰。

node_modules
    ---- bar
        ---- node_modules
            ---- foo@1.0.0
    ---- baz 
        ---- node_modules
            ---- foo@2.0.0

而在NPM3中,依赖的安装路径变成了扁平式的。所有的第三方依赖包都被放倒根目录下的node_modules中。这种方式适合前端项目:需要优化到很小代码空间占用量、以及打包分析等,同时顺带解决了Windows中文件路径不允许过长的问题。

只有在遇到存在冲突的情况下,才会出现嵌套。

NPM3的思想:尽可能的扁平化你的依赖。

CNPM

CNPM是阿里巴巴集团的Node.js团队研发的一套更快的NPM工具。它比NPM快不仅仅是依靠使用国内的镜像,还有就是在安装过程中,将一些包缓存到node_modules/.npminstall 目录下,再以符号链接(Symbol Link)的形式将依赖目录连接到对应的路径。也就是说,在实际安装过程中,相同版本的包只有一份实体,类似于NPM3。

Chrome V8

Node.js使用Chrome V8作为Javascript的解释器。

V8是一个高效的JavaScript引擎,不同于其他JS引擎的地方:

  1. JIT编译
  2. 垃圾回收
  3. 内敛缓存
  4. 隐藏类

JIT编译

JIT编译,Just-In-Time编译,即时编译。编译输出结果是机器语言,而不是字节码。

垃圾回收

V8的垃圾回收器借鉴了Java VM的精确垃圾回收管理,其垃圾回收机制的效率是相当高的。

内联缓存(Inline Cache)

如果访问过a.b,那么当再次访问a.b时,不会再对哈希表进行一次寻址,因为V8已经缓存过这个属性的偏移量,不用再次计算寻址的偏移量。

隐藏类

TODO: 隐藏类的具体机制还有待进一步查询资料。

V8引擎比较高效的机制主要是 内联缓存隐藏类

V8引擎遵循ECMA Script标准。Node.js会随着V8的更新升级自己的代码。

libuv

libuv 是一个专注于异步I/O的跨平台类库。最初是由Node的创始人Ryan Dahl为Node写的。只要是为Node开发,但是也可以支持其他项目。

libuv中一个重要的概念是 事件循环

libuv的一些特性

  • 基于epoll/kqueue/IOCP/event ports实现的全能事件循环;
  • 异步TCP 和 UDP套接字
  • 异步DNS解析
  • 异步文件、文件系统操作
  • 文件系统事件
  • ANSI转义码控制的TTY
  • 使用UNIX domain套接字或者命名管道实现的套接字共享IPC
  • 子进程
  • 线程池
  • 信号(Signal)处理
  • 高精度时钟
  • 线程和同步元
PS:不同的平台有不同的异步机制(如epoll、IOCP等),libuv 基于此实现来跨平台的事件循环。

另外libuv接口简洁明了,灵活性好,便于使用。Node中事件循环直接使用libuv的事件循环。

Node中其他一些依赖

http-parser

一个C实现的HTTP消息解析器,能解析HTTP协议的请求数据和返回数据。

OpenSSL

安全套接字层协议库。
SSL(Secure Socket Layer),可以在网络上提供秘密性的传输。用于对HTTP协议进行加密。

zlib

zlib是一个提供数据压缩功能的库。

IDE

神的编辑器和编辑器之神

  • VIM/NeoVIM
  • Emacs
  • Sublime Text
  • Visual Studio Code
  • Atom
  • Visual Studio

node-gyp

node-gyp 是 Node.js下的C++扩展构建工具。基于GYP(General Your Projects)进行工作,GYP时Google的一套构建工具。

node-gyp进行工作需要一些依赖。

在UNIX系列平台下:

  • python
  • make
  • C++编译工具包(GCC)

在macOS下:

  • xcode

在 Windows中

  • Visual Studio

也或者使用Visual C++ Build Tools(比较轻量级)。


胡斐
107 声望3 粉丝

前端大海里的小鱼